package com.jackstraw.jack_base.datasource.mybatisplus.base.service.impl;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jackstraw.jack_base.datasource.database.parten.base.entity.page.PageData;
import com.jackstraw.jack_base.datasource.mybatisplus.base.dao.MyBatisPlusDao;
import com.jackstraw.jack_base.datasource.mybatisplus.base.entity.po.MyBatisPlusPo;
import com.jackstraw.jack_base.datasource.mybatisplus.base.service.MyBatisPlusService;
import com.jackstraw.jack_base.datasource.mybatisplus.base.util.MyBatisPlusUtil;
import com.jackstraw.jack_base.datasource.mybatisplus.exception.BadMybatisPlusException;
import com.jackstraw.jack_base.util.CollectionUtil;
import com.jackstraw.jack_base.util.ObjectUtil;
import com.jackstraw.jack_base.util.StringUtil;
import com.jackstraw.jack_base.util.entity.DefaultConstants;
import com.jackstraw.jack_base.util.function.ReflectionUtil;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * Mybatis-Plus基本Service实现抽象类
 *
 * @param <DAO>
 * @param <PO>
 * @param <BO>
 * @param <ID>
 */
public class MyBatisPlusServiceImpl<DAO extends MyBatisPlusDao<PO>,
        PO extends ID,
        BO extends PO,
        ID extends MyBatisPlusPo<PO>>
        extends ServiceImpl<DAO, PO>
        implements MyBatisPlusService<DAO, PO, BO, ID> {

    /**
     * 业务操作类Class
     */
    private final Class<BO> boClass = currentBOClass();
    /**
     * 主键类型
     */
    private final Class<ID> idClass = currentIDClass();

    /**
     * 获取主键字段的名称
     */
    private final String idFieldName = currentIdFieldName();

    /**
     * 主键字段对应数据库的字段
     */
    private final String idColumnName = currentIdColumnName();

    private String currentIdColumnName() {
        List<Field> declaredFieldsAll = ReflectionUtil.getDeclaredFieldsAll(idClass);
        Optional<Field> optionalField = declaredFieldsAll.stream().filter(field -> field.isAnnotationPresent(TableId.class)).findFirst();
        if (optionalField.isPresent()) {
            Field field = optionalField.get();
            return field.getName();
        } else {
            throw new BadMybatisPlusException("001", "主键不存在");
        }
    }

    private String currentIdFieldName() {
        List<Field> declaredFieldsAll = ReflectionUtil.getDeclaredFieldsAll(idClass);
        Optional<Field> optionalField = declaredFieldsAll.stream().filter(field -> field.isAnnotationPresent(TableId.class)).findFirst();
        if (optionalField.isPresent()) {
            Field field = optionalField.get();
            TableId tableId = field.getAnnotation(TableId.class);
            return tableId.value();
        } else {
            throw new BadMybatisPlusException("001", "主键不存在");
        }
    }

    @Override
    public DAO getDao() {
        return this.getBaseMapper();
    }

    @Override
    public Class<PO> getPoClass() {
        return this.getEntityClass();
    }

    @Override
    public Class<BO> getBoClass() {
        return this.boClass;
    }

    @Override
    public Class<DAO> getDaoClass() {
        return this.mapperClass;
    }

    @Override
    public Class<ID> getIdClass() {
        return this.idClass;
    }

    public String getIdFieldName() {
        return idFieldName;
    }

    public String getIdColumnName() {
        return idColumnName;
    }


    /**
     * 拼接默认的查询条件
     *
     * @param params
     * @return
     */
    public QueryWrapper<PO> getWrapper(Map<String, Object> params) {
        String id = (String) params.get(this.getIdFieldName());
        QueryWrapper<PO> wrapper = new QueryWrapper<>();
        wrapper.eq(StringUtil.StringUtilsBylang3.isNotBlank(id), this.getIdColumnName(), id);
        return wrapper;

    }


    /**
     * 获取分页对象
     *
     * @param params 分页查询参数
     * @return
     */
    protected IPage<PO> getPage(Map<String, Object> params) {
        return getPage(params, null, Boolean.TRUE);
    }

    /**
     * 获取分页对象
     *
     * @param params            分页查询参数
     * @param defaultOrderField 默认排序字段 (优先与传入的参数，若与传入的参数相同，则按照传入的参数顺序排序)
     * @param isAsc             默认排序字段排序方式(true: 升序 false：倒序)
     */
    protected IPage<PO> getPage(Map<String, Object> params, String defaultOrderField, boolean isAsc) {
        //分页参数
        long current = 1;
        long size = 10;


        if (ObjectUtil.ObjectUtilByLang3.isNotEmpty(params.get(DefaultConstants.PAGE_NUM))) {
            current = (Integer) params.get(DefaultConstants.PAGE_NUM);
        }
        if (ObjectUtil.ObjectUtilByLang3.isNotEmpty(params.get(DefaultConstants.PAGE_SIZE))) {
            size = (Integer) params.get(DefaultConstants.PAGE_SIZE);
        }

        //分页对象
        Page<PO> page = new Page<>(current, size);

        String orderField = null;
        if (ObjectUtil.ObjectUtilByLang3.isNotEmpty(params.get(DefaultConstants.ORDER_FIELD))) {
            orderField = (String) params.get(DefaultConstants.ORDER_FIELD);
        }

        Map<String, String> orderMap = new HashMap<>();
        if (ObjectUtil.ObjectUtilByLang3.isNotEmpty(params.get(DefaultConstants.ORDER))) {
            orderMap = (Map<String, String>) params.get(DefaultConstants.ORDER);
        }

        List<String> orderFieldList = new ArrayList<>();
        if (StringUtil.StringUtilsBylang3.isNotEmpty(orderField)) {
            orderFieldList = Arrays.asList(orderField.split(","));
        }

        if (StringUtil.StringUtilsBylang3.isNotEmpty(defaultOrderField)) {
            if (!orderFieldList.contains(defaultOrderField)) {
                //默认排序
                if (isAsc) {
                    page.addOrder(OrderItem.asc(defaultOrderField));
                } else {
                    page.addOrder(OrderItem.desc(defaultOrderField));
                }
            }
        }

        Map<String, String> finalOrderMap = orderMap;
        orderFieldList.stream().forEach(field -> {
            String order = finalOrderMap.get(field);
            if (DefaultConstants.ASC.equalsIgnoreCase(order)) {
                page.addOrder(OrderItem.asc(field));
            } else {
                page.addOrder(OrderItem.desc(field));
            }
        });

        return page;
    }

    /**
     * 获取id的值
     *
     * @param id
     * @return
     */
    protected Serializable getIdValue(ID id) {
        return (Serializable) ReflectionUtil.getValue(this.getIdFieldName(), id);
    }

    /**
     * 批量获取ID的值
     *
     * @param idS
     * @return
     */
    protected List<Serializable> getIdValue(Collection<ID> idS) {
        if (CollectionUtil.isEmpty(idS)) {
            return new ArrayList<>();
        }
        return idS.stream().map(id -> getIdValue(id)).collect(Collectors.toList());
    }


    @Override
    public PageData<BO> page(Map<String, Object> params) {
        IPage<PO> poIPage = this.getBaseMapper().selectPage(getPage(params), getWrapper(params));
        PageData<BO> pageData = MyBatisPlusUtil.pageData(poIPage, this.getBoClass());
        return pageData;
    }

    @Override
    public List<BO> list(Map<String, Object> params) {
        List<PO> poList = this.getBaseMapper().selectList(getWrapper(params));
        return this.toBo(poList);
    }

    @Override
    public BO get(ID id) {
        Serializable idValue = this.getIdValue(id);
        PO po = this.getBaseMapper().selectById(idValue);
        return this.toBo(po);
    }

    @Override
    public List<BO> get(Collection<ID> idS) {
        List<PO> poList = this.getBaseMapper().selectBatchIds(this.getIdValue(idS));
        return this.toBo(poList);
    }

    @Override
    public void insert(BO bo) {
        this.save(bo);
    }

    @Override
    public List<BO> insertBatch(List<BO> boS) {
        List<PO> pos = this.toPo(boS);
        this.saveBatch(pos);
        return toBo(pos);
    }

    @Override
    public void update(BO bo) {
        this.saveOrUpdate(bo);
    }

    @Override
    public void updateBatch(List<BO> boS) {
        List<PO> pos = this.toPo(boS);
        this.saveOrUpdateBatch(pos);
    }

    @Override
    public void delete(ID id) {
        this.removeById(this.getIdValue(id));
    }

    @Override
    public void deleteBatch(List<ID> idS) {
        this.removeBatchByIds(this.getIdValue(idS));
    }

}
